import string
import math
import ast
import copy
from util import *
from mucker import *


#Functions for Experiment
def counter(dataSet, quiet = 0): #0 = quiet
    report = Report()
    report.id = dataSet.id
    if quiet == 1:
        print "DataSet: " + dataSet.name
    report.dsName = dataSet.name[:-4] #[:-4] removes .csv
    problemFeatures = 0
    problemInstances = 0
    #Number of Features
    report.numFeats = numFeats(dataSet, quiet)
    #Number of Instances
    report.numCases = numInst(dataSet, quiet)
    #Number of Constant Features/IdenticalFeatures
    report.numConstFeats = numConstFeatures(dataSet, quiet)
    problemFeatures += report.numConstFeats
    if report.numConstFeats >= 2:
        report.numIdenticalFeats = report.numConstFeats
    #Missing Features/Instances Missing Values
    temp = numMissing(dataSet, quiet)
    report.numMissingFeats = temp[0]
    report.numCasesMissingVals = temp[1]
    problemFeatures += temp[0]
    problemInstances += temp[1]
    #Number of Duplicate Instances/Cases
    temp = numDupeInstances(dataSet, quiet)
    problemInstances += temp
    report.numIdenticalCases = temp
    #Number of Inconsistant Instances/Cases
    temp = numIncInstances(dataSet, quiet)
    problemInstances += temp
    report.numIncCases = temp
    #Implausible Features/Instances
    temp = numImplausibleFeats(dataSet)
    problemInstances += temp[1]
    report.numImplausibleFeats = temp[0]
    report.numCasesImplausibleVals = temp[1]
    #DataQualityCases
    report.numDataQualityCases = report.numCasesMissingVals + report.numCasesImplausibleVals #as per problem definition
    if quiet == 1:
        print "#Problem Features : %s" % problemFeatures
        print "#Problem Instances: %s" % problemInstances
    report.totalProblemFeats = problemFeatures
    report.totalProblemCases = problemInstances
    return report 



#Functions related to counter()
def numFeats(dataSet, quiet = 0): 
    #Counts the number of features in the dataSet manually
    numFeatures = 0
    for item in dataSet.features:
        numFeatures += 1
    if quiet == 1:
        print "#Features: %s" % numFeatures
    return numFeatures

def numInst(dataSet, quiet = 0):
    #Counts the number of instances in the dataSet manually
    numInstances = 0
    for item in dataSet.instances:
        numInstances += 1
    if quiet == 1:
        print "#Instances: %s" % numInstances
    return numInstances

def numConstFeatures(dataSet, quiet = 0):
    #Handels the looping for countConstFeatures()
    constFeatures = 0
    constFeatureHeaders = ""
    for item in dataSet.instances:
        temp = countConstFeatures(item, dataSet)
        constFeatures = temp[0]
        constFeatureHeaders = temp[1]
    if quiet == 1:
        print "#Constant Features: %s Headers: %s" % (constFeatures, constFeatureHeaders)
    return constFeatures

def countConstFeatures(instance, dataSet):
    #Counts Consistant Feature Values (value is the same for every instance)
    numConst = 0
    headers  = []
    index    = 0
    const    = 0
    nope     = 0
    for feat in instance.features:
        for inst in dataSet.instances:
            if feat.value == inst.features[index].value and feat.header == inst.features[index].header:
                numConst += 1
        if numConst == len(dataSet.instances):
            const += 1
            headers.append(feat.header)
        else:
            nope += 1 #for testing
        index += 1
        numConst = 0
    return const, headers

def numMissing(dataSet, quiet):
    #handels the looping for countMissingFeatVals()
    numMissing = 0
    numMissingInstances = 0
    for item in dataSet.instances:
        temp  = countMissingFeatVals(item)
        missingPerInstance   = temp[0]
        missingHeader        = temp[1]
        numMissingInstances += temp[2]
        if missingPerInstance > 0:
            if quiet == 1:
                print "---@ID: %s #Missing Values %s Headers %s" % (item.id, missingPerInstance, missingHeader)
            numMissing += len(missingHeader)
    return numMissing, numMissingInstances

def countMissingFeatVals(instance):
    #Counts missing feature values within an instance
    numMissing         = 0
    numMissingInstance = 0
    featHeader         = []
    for feat in instance.features:
            if feat.value is None or feat.value == '?' or feat.value == '':
                numMissing += 1
                featHeader.append(feat.header)
    if numMissing >= 2:
        numMissingInstance += 1
    return numMissing, featHeader, numMissingInstance
  
def numDupeInstances(dataSet, quiet = 0):
    #Counts the number of duplicate instances
    if quiet == 1:
        print "List of Duplicate Instances: "
    numDupes = 0
    for item in dataSet.instances:
        dupes = countDupeInstances(item, dataSet)
        if dupes > 0:
            if quiet == 1:
                print "---@ID: %s #Duplicates: %s" % (item.id, dupes)
            numDupes += 1
    if quiet == 1:
        print "#Duplicate Instances: %s" % numDupes
    return numDupes

def numIncInstances(dataSet, quiet):
    #Counts the number of inconsistant instances
    numInc = 0
    for item in dataSet.instances:
        inc = countIncInstances(item, dataSet)
        if inc > 0:
            if quiet == 1:
                print "---@ID: %s #Inconsistant: %s" % (item.id, inc)
            numInc += 1
    if quiet == 1:
        print "#Inconsistant Instances: %s" % numInc
    return numInc

def countDupeInstances(instance, dataSet):
    #Detects then counts the number of duplicate instances
    numDupes = 0
    temp     = instance
    for item in dataSet.instances:
        tempHeaderList = []
        tempValueList  = []
        itemHeaderList = []
        itemValueList  = []
        for feat in temp.features:
            tempHeaderList.append(feat.header)
            tempValueList.append(feat.value)
        for feat in item.features:
            itemHeaderList.append(feat.header)
            itemValueList.append(feat.value)
        if tempValueList == itemValueList and tempHeaderList == itemHeaderList:
            numDupes += 1
    if numDupes >= 1:
        numDupes = numDupes -1 #this is to account for comparing the item to itself
    return numDupes

def countIncInstances(instance, dataSet):
    #Detects then counts the number of inconsistant instances
    numInc = 0
    temp = instance
    for item in dataSet.instances:
        tempHeaderList = []
        tempValueList = []
        itemHeaderList = []
        itemValueList = []
        for feat in temp.features[:len(temp.features) -1]:
            tempHeaderList.append(feat.header)
            tempValueList.append(feat.value)
        for feat in item.features[:len(item.features) -1]:
            itemHeaderList.append(feat.header)
            itemValueList.append(feat.value)
        if tempValueList == itemValueList and tempHeaderList == itemHeaderList and item.features[-1].value != temp.features[-1].value:
            numInc += 1
    return numInc

def numImplausibleFeats(dataSet):
    #Counts Implausible Feature Values
    allVals         = dumpFeatVals(dataSet)
    instances       = []
    numImp          = 0
    numImpInstances = 0
    #print allVals
    for feature in dataSet.instances[0].features:
        for instance in dataSet.instances:
            vals = copy.deepcopy(allVals[feature.id])
            temp = findRange(vals)
            min = temp[0]
            selected = instance.features[feature.id].value
            max = temp[1]
            if isNum(selected) == True:
                if selected > max or selected < min:
                    instances.append(instance)
                    numImp += 1
        if len(instances) > 2:
            numImpInstances += 1
        instances = []
            
    return numImp, numImpInstances
        
         
        
#MISC

def calcMean(numList):
    #Calculates the mean of a list of numbers
    n = len(numList)
    sum = 0.0
    for num in numList:
        if num == '':
            num = '0'
        sum += ast.literal_eval(num)
    return sum/n
    
def calcSTD(numList):
    #Calculates the Standard Deviation of a list of numbers
    mean = calcMean(numList)
    n = len(numList)
    sum = 0.0
    for num in numList:
        if num == '':
            num = '0'
        sum += ((ast.literal_eval(num) - mean)**2)
    return math.sqrt((1.0/(n))*(sum))

def findRange(numList):
    #Finds the the range of values in a given number list
    numList.sort()
    size = len(numList)
    c    = int(size*0.01) # 05%
    min  = numList[c]
    c    = int(size*0.99) # 95%
    max  = numList[c]
    #print 'min: %s ... max: %s' % (min,max)
    return min, max
       

#Report Functions
def generateCounterReport(dataSet):
    report = counter(dataSet)
    if report.id == 0:
        print string.ljust('DS', 20), string.ljust('TF', 3), string.ljust('IF', 3), string.ljust('CF', 3), string.ljust('MF', 3), string.ljust('CFV', 3), string.ljust('IFV', 3), string.ljust('TPF', 3), string.ljust('TC', 3), string.ljust('IC', 3), string.ljust('InC', 3), string.ljust('CMV', 3), string.ljust('CCV', 3), string.ljust('CIV', 3), string.ljust('DQC', 3) , string.ljust('TPC', 3)
    print string.ljust(report.dsName, 20), string.ljust(`report.numFeats`, 3), string.ljust(`report.numIdenticalFeats`, 3), string.ljust(`report.numConstFeats`, 3), string.ljust(`report.numMissingFeats`, 3), string.ljust(`report.numConflictFeats`, 3), string.ljust(`report.numImplausibleFeats`, 3), string.ljust(`report.totalProblemFeats`, 3), string.ljust(`report.numCases`, 3), string.ljust(`report.numIdenticalCases`, 3), string.ljust(`report.numIncCases`, 3), string.ljust(`report.numCasesMissingVals`, 3), string.ljust(`report.numCasesConflictVals`, 3), string.ljust(`report.numCasesImplausibleVals`, 3), string.ljust(`report.numDataQualityCases`, 3), string.ljust(`report.totalProblemCases`, 3)
    if report.id == len(dataSetList):
        print "\nLegend: \nDS  = DataSet\nTF  = TotalFeatures\nCF  = ConstantFeatures"
        print "MF  = MissingFeatures\nCFV = ConflictingFeatures\nIFV = ImplausibleFeatures"
        print "TPF = TotalProblemFeatures\nTC  = TotalCases\nIC  = IdenticalCases"
        print "InC = InconsistantCases\nCMV = CasesMissingValues\nCCV = ConflictingCases"
        print "CIV = ImplausibleCases\nDQC = DataQualityCases\nTPC = TotalProblemCases"


#Other

def runAll(list):
    itemIndex = 0
    for item in list:
        tempds = loadDataSet(item)
        tempds.id = itemIndex
        generateCounterReport(tempds)
        itemIndex += 1


#DEBUG AREA
#findSTD()
#runAll(dirtyDataSetList)
ds = loadDataSet(dataSetList[2])
#print ds.instances[0].id
#counter(ds)
#generateCounterReport(ds)
#temp = dumpFeatVals(ds)
#print(calcMean(temp[5]))
#print(calcSTD(temp[5]))
#findRange(temp[23])
#print ds.instances[0].features[0].value
#numImplausibleFeats(ds)
#convertDataSet(ds)
mucker(ds, 3)
generateCounterReport(ds)
